package main.java.LeetCode.ClassicalAlgorithm.Timer;

import jdk.internal.misc.Unsafe;

import java.util.LinkedList;
import java.util.List;


@SuppressWarnings("ALL")
public class TimingWheel<T> {
    private Node[] wheel;

    private int time;

    private static final Unsafe U;

    private final int ASHIFT;
    private final int ABASE;

    static {
        U = Unsafe.getUnsafe();
    }

    private TimingWheel(int time){
        this.time = time;
        wheel = new Node[time];
        ABASE = U.arrayBaseOffset(Node[].class);

        int scale = U.arrayIndexScale(Node[].class);
        if ((scale & (scale - 1)) != 0)
            throw new ExceptionInInitializerError("array index scale not a power of two");

        ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
    }

    public TimingWheel(){
        this(60);
    }


    private static class Node<T>{
        T r;
        Node<T> next;

        public Node(T r,Node<T> next){
            this.r = r;
            this.next = next;
        }

        public Node(T r){
            this(r,null);
        }
    }


    public void set(int index,T t){

        boolean isAdd = false;
        Node<T> node = new Node<>(t);
        Node<T> head;

        while (true){

            if((head=wheelAt(index))==null&&casWheelAt(index,null,node))
                break;

            synchronized (head){
                if(head == wheelAt(index)){
                    node.next = head;
                    wheel[index] = node;
                    isAdd = true;
                }
            }

            if(isAdd)
                break;
        }

    }

    public T get(int index){
        Node<T> head;

        boolean isGet = false;

        while (true){
            if((head = wheelAt(index)) == null)
                return null;

            synchronized (head){
                if(head == wheelAt(index)){
                    Node<T> next = head.next;
                    head.next = null;
                    wheel[index] = next;
                    isGet = true;
                }
            }

            if(isGet)
                break;
        }
        T t = head.r;
        head = null;
        return t;
    }

    public List<T> getAll(int index){

        Node<T> head;
        LinkedList<T> list = new LinkedList<>();
        boolean isGet = false;

        while (true) {
            if ((head = wheelAt(index)) == null)
                return list;

            synchronized (head){
                if(head == wheelAt(index)) {
                    wheel[index] = null;
                    isGet = true;
                }
            }

            if(isGet)
                break;
        }

        while (head!=null){
            list.add(head.r);
            head = head.next;
        }

        return list;
    }

    private boolean casWheelAt(int index,Node<T> expect,Node<T> value){
        return U.compareAndSetReference(wheel, ((long)index << ASHIFT) + ABASE, expect, value);
    }

    private Node<T> wheelAt(int index){
        return (Node<T>)U.getReferenceAcquire(wheel, ((long)index << ASHIFT) + ABASE);
    }

    public static void main(String[] args) {
        TimingWheel<Integer> timingWheel = new TimingWheel<>();
        timingWheel.set(1,4);
        System.out.println(timingWheel.get(1));
    }
}